/**
 * 
 */
package jvs.peer.io;

import java.io.IOException;
import java.io.OutputStream;

/**
 * @author qiangli
 * 
 */
public class ChunkedOutputStream extends OutputStream {
	private static final byte[] CRLF = { '\r', '\n' };

	private static final int BUFSIZE = 1024;

	private OutputStream out;

	private byte[] buf;

	private int pos;
	
	private boolean close;

	public ChunkedOutputStream(OutputStream out) {
		this(out, BUFSIZE, false);
	}
	
	public ChunkedOutputStream(OutputStream out, boolean close) {
		this(out, BUFSIZE, close);
	}

	public ChunkedOutputStream(OutputStream out, int bufsize, boolean close) {
		this.out = out;
		this.buf = new byte[bufsize];
		this.pos = 0;
		this.close = close;
	}

	@Override
	public void write(int b) throws IOException {
		buf[pos++] = (byte) b;
		if (pos == buf.length) {
			flush();
		}
	}

	@Override
	public void write(byte[] b, int off, int len) throws IOException {
		for (int i = 0; i < len; i++) {
			write(b[off + i]);
		}
	}

	@Override
	public void write(byte[] b) throws IOException {
		write(b, 0, b.length);
	}

	@Override
	public void close() throws IOException {
		if (out == null) {
			return;
		}
		try {
			flush();
			//
			writeLastChunk();
			writeTrailer();
			terminate();
			if (close) {
				out.close();
			}
		} finally {
			out = null;
			buf = null;
		}
	}

	@Override
	public void flush() throws IOException {
		if (pos > 0) {
			writeChunk();
			pos = 0;
		}
	}

	private void writeChunk() throws IOException {
		out.write(chunksize());
		out.write(CRLF);
		out.write(buf, 0, pos);
		out.write(CRLF);
		out.flush();
	}
	
	private void writeLastChunk() throws IOException {
		out.write('0');
		out.write(CRLF);
		out.flush();
	}
	
	private void writeTrailer() {
		//TODO
	}
	
	private void terminate() throws IOException {
		out.write(CRLF);
		out.flush();
	}

	private byte[] chunksize() {
		return Integer.toHexString(pos).getBytes();
	}
}
